package com.mtw.bbs.article.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mtw.bbs.article.pojo.elasticsearch.TopicESIndex;
import com.mtw.bbs.article.repository.TopicESRepository;
import com.mtw.bbs.constant.TopicConstant;
import com.mtw.bbs.article.pojo.entity.TopicIndex;
import com.mtw.bbs.article.mapper.TopicIndexMapper;
import com.mtw.bbs.article.pojo.entity.TopicInfo;
import com.mtw.bbs.article.pojo.entity.TopicMember;
import com.mtw.bbs.article.pojo.form.TopicForm;
import com.mtw.bbs.article.pojo.vo.TopicAdminVo;
import com.mtw.bbs.article.pojo.vo.TopicMemberVo;
import com.mtw.bbs.article.pojo.vo.UserTopicVo;
import com.mtw.bbs.article.service.TopicInfoService;
import com.mtw.bbs.article.service.TopicMemberService;
import com.mtw.bbs.article.service.TopicIndexService;
import com.mtw.bbs.article.pojo.vo.TopicVo;
import com.mtw.bbs.common.core.constant.ServiceConstant;
import com.mtw.bbs.common.core.security.UserDetails;
import com.mtw.bbs.common.core.security.context.SecurityContextHolder;
import com.mtw.bbs.common.core.util.BeanCopyUtils;
import com.mtw.bbs.common.core.util.ListUtilz;
import com.mtw.bbs.common.core.vo.Result;
import com.mtw.bbs.common.redisBase.util.RedisUtilz;
import com.mtw.bbs.fileAdmin.feign.FileClient;
import com.mtw.bbs.fileAdmin.pojo.vo.FileVo;
import com.mtw.bbs.userAdmin.pojo.vo.UserInfoVo;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 话题
 */
@Slf4j
@Service("topicIndexService")
@RequiredArgsConstructor
public class TopicIndexServiceImpl extends ServiceImpl<TopicIndexMapper, TopicIndex> implements TopicIndexService {

    private final FileClient fileClient;
    private final TopicIndexMapper topicIndexMapper;
    private final TopicInfoService topicInfoService;
    private final ThreadPoolTaskExecutor bizExecutor;
    private final TopicESRepository topicESRepository;
    private final TopicMemberService topicMemberService;
    private final RedisUtilz<String, String> redisUtilz;

    @Lazy
    @Resource
    private  TopicIndexServiceImpl own;



    /**
     * 管理分页获取话题
     * @param page          当前页
     * @param size          分页大小
     * @param qw            查询条件
     * @return              分页结果
     */
    @Override
    public Page<TopicAdminVo> getAdminPage(int page, int size, QueryWrapper<TopicIndex> qw) {
        Page<TopicAdminVo> adminPage = topicIndexMapper.getAdminPage(new Page<>(page, size), qw);
        if (CollectionUtil.isNotEmpty(adminPage.getRecords())){
            String[] fileIds = adminPage.getRecords().stream().map(TopicAdminVo::getCover).filter(StringUtils::isNotBlank).toArray(String[]::new);
            Result<List<FileVo>> fileUrls = fileClient.getFileUrlByIdArray(fileIds);
            Map<String, String> urlMap;
            if (fileUrls.isSuccess()){
                urlMap = fileUrls.getData().stream().collect(HashMap::new, (map, item) -> map.put(item.getId(), item.getUrl()), HashMap::putAll);
            }else {
                urlMap = new HashMap<>(1);
            }
            adminPage.getRecords().forEach(x->{
                if (urlMap.get(x.getCover()) != null){
                    x.setCover(urlMap.get(x.getCover()));
                }
            });
        }
        return adminPage;
    }



    /**
     * 分页获取话题
     *
     * @param page 分页对象
     * @param qw   查询条件
     * @return 分页结果
     */
    @Override
    public Page<TopicVo> getTopicVoPage(int page, int size, QueryWrapper<TopicIndex> qw) {

        long s = System.currentTimeMillis();
        log.info("开始查询话题分页，page:{},size:{},s:{}",page,size,s);
        Page<TopicVo> voPage = topicIndexMapper.getTopicVoPage(new Page<>(page, size), qw);
        long e1 = System.currentTimeMillis();
        log.info("查询话题分页结束，耗时：{}",e1-s);

        if (CollectionUtil.isNotEmpty(voPage.getRecords())) {
            UserDetails details = SecurityContextHolder.getContext().getAuthentication();

            // 共享线程数据
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

            // 是否已加入话题
            CompletableFuture<Boolean> joinF = CompletableFuture.supplyAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                if (details != null) {
                    for (TopicVo record : voPage.getRecords()) {
                        record.setJoined(topicMemberService.userJoinedTopic(details.getId(), record.getId()) != -1);
                    }
                }
                return true;
            }, bizExecutor);

            // 设置话题封面
            setCoverUrl(voPage.getRecords());
            try {
                joinF.get(8, TimeUnit.SECONDS);
            }catch (Exception e){
                log.error("异步获取用户加入话题异常",e);
            }
        }
        return voPage;
    }



    /**
     * 分页获取简单话题信息
     * @param page          当前页
     * @param size          分页大小
     * @param qw            查询条件
     * @return              分页结果
     */
    @Override
    public Page<TopicVo> getSimpleTopicVoPage(int page, int size, QueryWrapper<TopicIndex> qw) {
        Page<TopicVo> voPage = topicIndexMapper.getTopicVoPage(new Page<>(page, size), qw);
        if (CollectionUtil.isNotEmpty(voPage.getRecords())) {
            setCoverUrl(voPage.getRecords());
        }
        return voPage;
    }


    /**
     * 分页获取用户加入的话题
     *
     * @param page 当前页
     * @param size 分页大小
     * @param isAdmin       是否查管理的，true为查用户管理的话题，false为全部
     * @param userId        用户id
     * @return 分页结果
     */
    @Override
    public Page<UserTopicVo> getUserJoinTopicVoPage(int page, int size, boolean isAdmin,String userId) {

        List<TopicMember> list;
        if (isAdmin){
            list  = topicMemberService.getTopicListByUser(userId,true).stream().filter(x->x.getMemberType() <2)
                                        .sorted(Comparator.comparingLong(x->x.getCreatedDate().getTime())).toList();
        }else {
            list = topicMemberService.getTopicListByUser(userId,true).stream().sorted(Comparator.comparingLong(x->x.getCreatedDate().getTime())).toList();
        }

        ListUtilz<TopicMember> listUtilz = new ListUtilz<>();
        list = listUtilz.subListPage(list,page,size);
        if (CollectionUtil.isEmpty(list)){
            return new Page<>(page,size);
        }

        QueryWrapper<TopicIndex> qw = new QueryWrapper<>();
        qw.eq("t.status", ServiceConstant.STATUS_ENABLE);
        qw.in("t.id",list.stream().map(TopicMember::getTopicId).toList());

        Page<TopicVo> indexPage = topicIndexMapper.getTopicVoPage(new Page<>(page, size), qw);
        Page<UserTopicVo> voPage = new Page<>(page, size, indexPage.getTotal());
        if (CollectionUtil.isNotEmpty(indexPage.getRecords())) {

            String[] fileIds = indexPage.getRecords().stream().map(TopicVo::getCover).filter(StringUtils::isNotBlank).toArray(String[]::new);
            Result<List<FileVo>> fileUrls = fileClient.getFileUrlByIdArray(fileIds);
            Map<String, String> urlMap;
            if (fileUrls.isSuccess()){
                urlMap = fileUrls.getData().stream().collect(HashMap::new, (map, item) -> map.put(item.getId(), item.getUrl()), HashMap::putAll);
            }else {
                urlMap = new HashMap<>(1);
            }

            HashMap<String, UserTopicVo> voMap = new HashMap<>(size);
            indexPage.getRecords().forEach(x -> {
                UserTopicVo vo = BeanCopyUtils.copyBean(x, UserTopicVo.class);
                if (urlMap.get(x.getCover()) != null){
                    vo.setCover(urlMap.get(x.getCover()));
                }
                voMap.put(x.getId(),vo);
            });
            List<UserTopicVo> topicVos = list.stream().map(x -> {
                UserTopicVo vo = voMap.get(x.getTopicId());
                if (vo == null) {
                    return null;
                }
                vo.setMemberType(x.getMemberType());
                return vo;
            }).filter(Objects::nonNull).toList();
            voPage.setRecords(topicVos);
        }
        return voPage;
    }


    /**
     * 获取话题详情
     *
     * @param id 话题id
     * @return 话题VO
     */
    @Override
    @Cacheable(value = "topicInfo", key = "#id")
    public TopicVo getTopicInfo(String id) {
        TopicVo vo = topicIndexMapper.getOneById(id);
        setCoverUrl(vo);
        return vo;
    }




    /**
     *  获取话题详情,并带出话题成员
     * @param id        主键id
     * @return          话题vo
     */
    @Override
    @Cacheable(value = "topicWithMember", key = "#id")
    public TopicVo getTopicInfoWithMember(String id) {


        // 查话题成员
        CompletableFuture<List<UserInfoVo>> memberF = CompletableFuture.supplyAsync(() -> {
            LambdaQueryWrapper<TopicMember> qw = new LambdaQueryWrapper<>();
            qw.eq(TopicMember::getTopicId, id);
            Page<TopicMemberVo> memberPage = topicMemberService.getTopicMemberPage(1, 2, id);
            return memberPage.getRecords().stream().map(TopicMemberVo::getPoster).collect(Collectors.toList());
        }, bizExecutor);

        TopicVo vo = own.getTopicInfo(id);

        try {
            List<UserInfoVo> userInfoVos = memberF.get(8, TimeUnit.SECONDS);
            if (vo != null && CollectionUtil.isNotEmpty(userInfoVos)){
                vo.setMemberList(userInfoVos);
            }
        }catch (Exception e){
            log.error("异步获取话题成员异常",e);
        }

        return vo;
    }



    /**
     * 设置话题封面url
     */
    public void setCoverUrl(List<TopicVo> list){

        String[] fileIds = list.stream().map(TopicVo::getCover).filter(StringUtils::isNotBlank).toArray(String[]::new);
        Result<List<FileVo>> fileUrls = fileClient.getFileUrlByIdArray(fileIds);
        Map<String, String> urlMap;
        if (fileUrls.isSuccess()){
            urlMap = fileUrls.getData().stream().collect(HashMap::new, (map, item) -> map.put(item.getId(), item.getUrl()), HashMap::putAll);
        }else {
            urlMap = new HashMap<>(1);
        }
        list.forEach(x->{
            if (urlMap.get(x.getCover()) != null){
                x.setCover(urlMap.get(x.getCover()));
            }
        });
    }


    /**
     * 设置话题封面url
     */
    private void setCoverUrl(TopicVo vo){
        if (StringUtils.isNotBlank(vo.getCover())){
            Result<FileVo> fileUrl = fileClient.getUrl(vo.getCover());
            if (fileUrl.isSuccess() && fileUrl.getData() != null && fileUrl.getData().getUrl() != null){
                vo.setCover(fileUrl.getData().getUrl());
            }
        }
    }




    /**
     * 新增话题
     */
    @Override
    @Transactional
    public boolean saveTopic(TopicForm form) {

        UserDetails details = SecurityContextHolder.getContext().getAuthentication();
        redisUtilz.delete(TopicConstant.REDIS_USER_JOIN_TOPIC_PREFIX + details.getId());
        TopicIndex entity = new TopicIndex();
        BeanUtils.copyProperties(form, entity);
        entity.setStatus(ServiceConstant.STATUS_ENABLE);
        boolean saved = save(entity);
        if (saved){
            // 话题成员
            TopicMember member = new TopicMember();
            member.setUserId(details.getId());
            member.setTopicId(entity.getId());
            member.setMemberType(0);
            member.setCreatedDate(new Date());
            topicMemberService.save(member);

            // 话题信息
            TopicInfo info = BeanCopyUtils.copyBean(form, TopicInfo.class);
            info.setId(entity.getId());
            info.setTopicMaster(details.getId());
            info.setMemberCount(1L);
            topicInfoService.save(info);

            // 话题信息保存es
            TopicESIndex esIndex = new TopicESIndex();
            esIndex.setCover(entity.getCover());
            esIndex.setId(entity.getId());
            esIndex.setName(entity.getName());
            esIndex.setStatus(entity.getStatus());
            topicESRepository.save(esIndex);

            redisUtilz.delete(TopicConstant.REDIS_USER_JOIN_TOPIC_PREFIX + details.getId());
        }
        return saved;
    }



    /**
     * 新增话题，管理员新增
     */
    @Override
    public boolean saveTopicByAdmin(TopicForm form) {

        TopicIndex entity = new TopicIndex();
        BeanUtils.copyProperties(form, entity);
        entity.setStatus(ServiceConstant.STATUS_ENABLE);
        boolean saved = save(entity);
        if (saved){
            TopicInfo info = BeanCopyUtils.copyBean(form, TopicInfo.class);
            info.setId(entity.getId());
            info.setMemberCount(0L);
            topicInfoService.save(info);

            // 话题信息保存es
            TopicESIndex esIndex = new TopicESIndex();
            esIndex.setCover(entity.getCover());
            esIndex.setId(entity.getId());
            esIndex.setName(entity.getName());
            esIndex.setStatus(entity.getStatus());
            topicESRepository.save(esIndex);
        }
        return saved;
    }


    /**
     * 更新话题
     */
    @Override
    @Transactional
    @Caching(evict = {
            @CacheEvict(value = "topicInfo",  key = "#form.id"),
            @CacheEvict(value = "topicWithMember",  key = "#form.id")
    })
    public boolean updateTopic(TopicForm form) {

        TopicIndex index = getById(form.getId());
        if (index == null){
            return false;
        }


        // 更新话题封面
        if (StringUtils.isNotBlank(form.getCover())){
            LambdaUpdateWrapper<TopicIndex> luw = new LambdaUpdateWrapper<>();
            luw.set(TopicIndex::getCover,form.getCover());
            luw.eq(TopicIndex::getId,form.getId());
            update(luw);


            // 更新ES话题信息
            TopicESIndex esIndex = new TopicESIndex();
            esIndex.setCover(form.getCover());
            esIndex.setId(index.getId());
            esIndex.setName(index.getName());
            esIndex.setStatus(index.getStatus());
            topicESRepository.save(esIndex);
        }


        // 更新话题信息
        if (StringUtils.isNotBlank(form.getMemberNickname()) || StringUtils.isNotBlank(form.getDescription())){
            LambdaUpdateWrapper<TopicInfo> iuw = new LambdaUpdateWrapper<>();
            iuw.set(StringUtils.isNotBlank(form.getMemberNickname()),TopicInfo::getMemberNickname,form.getMemberNickname());
            iuw.set(StringUtils.isNotBlank(form.getDescription()),TopicInfo::getDescription,form.getDescription());
            iuw.eq(TopicInfo::getId,form.getId());
            topicInfoService.update(iuw);
        }


        return true;
    }



    /**
     * 更新话题，管理员更新
     */
    @Override
    @Caching(evict = {
            @CacheEvict(value = "topicInfo",  key = "#form.id"),
            @CacheEvict(value = "topicWithMember",  key = "#form.id")
    })
    public boolean updateByAdmin(TopicForm form) {

        TopicIndex index = getById(form.getId());
        if (index == null){
            return false;
        }

        TopicIndex entity = BeanCopyUtils.copyBean(form, TopicIndex.class);
        BeanUtils.copyProperties(form, entity);
        updateById(entity);
        TopicInfo info = BeanCopyUtils.copyBean(form, TopicInfo.class);
        topicInfoService.updateById(info);

        // 更新ES话题信息
        TopicESIndex esIndex = new TopicESIndex();
        esIndex.setCover(StringUtils.isNotBlank(form.getCover())?index.getCover():form.getCover());
        esIndex.setId(form.getId());
        esIndex.setName(StringUtils.isNotBlank(form.getName())?index.getName():form.getName());
        esIndex.setStatus(form.getStatus()==null?index.getStatus():form.getStatus());
        topicESRepository.save(esIndex);

        return true;
    }






    /**
     * 删除话题
     * @param id 话题id
     */
    @Override
    @Caching(evict = {
            @CacheEvict(value = "topicInfo",  key = "#id"),
            @CacheEvict(value = "topicWithMember",  key = "#id")
    })
    public boolean deleteTopicById(String id){
        boolean result = removeById(id);
        if (result){
            topicESRepository.deleteById(id);
        }
        return result;
    }


}
